using System;
using System.Collections;
using System.Reflection;

namespace NUtil.beans {
    public class GraphBrowser {
        public GraphBrowser(NodeVisitor nodeVisitor) {
            _nodeVisitor = nodeVisitor;
            _visitedObjects = new ArrayList();
        }

        public delegate void NodeVisitor(object ob);

        public void Browse(object objectToBrowsed) {
            if (mayNotHaveAnyChilden(objectToBrowsed.GetType())) {
                throw new Exception(MethodBase.GetCurrentMethod().Name + " can not be called with value objects");
            }
            if (!contains(_visitedObjects, objectToBrowsed)) {
                _nodeVisitor(objectToBrowsed);
                _visitedObjects.Add(objectToBrowsed);
                browseChildren(objectToBrowsed);
            }
        }

        private bool contains(ArrayList list, object objectToBrowsed) {
            foreach (object o in list) {
                if (o == objectToBrowsed) {
                    return true;
                }
            }
            return false;
        }

        private void browseChildren(object objectToClone) {
            FieldInfo[] fieldInfos = getAllInstanceFields(objectToClone);
            foreach (FieldInfo fieldInfo in fieldInfos) {
                if (mayNotHaveAnyChilden(fieldInfo.FieldType)) {
                }
                else if (fieldInfo.FieldType.Equals(typeof (IList))) {
                    IList sourceList =
                        (IList) fieldInfo.GetValue(objectToClone);
                    if (sourceList != null) {
                        handleListSourceObjects(sourceList);
                    }
                }
                else {
                    object value = fieldInfo.GetValue(objectToClone);
                    if (value != null) {
                        Browse(value);
                    }
                }
            }
        }

        private void handleListSourceObjects(IList sourceList) {
            foreach (object listItem in sourceList) {
                if (mayNotHaveAnyChilden(listItem.GetType())) {
                }
                else {
                    Browse(listItem);
                }
            }
        }

        private FieldInfo[] getAllInstanceFields(object objectToClone) {
            return
                objectToClone.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        }

        private bool mayNotHaveAnyChilden(Type type) {
            return type.IsValueType || typeof (String).Equals(type) || typeof (Guid).Equals(type);
        }

        private ArrayList _visitedObjects;
        private NodeVisitor _nodeVisitor;
    }
}